package com.jiangyg.mall.authz.config;

import com.jiangyg.mall.authz.constant.SecurityConstant;
import com.jiangyg.mall.authz.service.MemberUserDetailsService;
import com.jiangyg.mall.authz.support.authentication.AccessDeniedExceptionHandler;
import com.jiangyg.mall.authz.support.authentication.AuthErrorExceptionHandler;
import com.jiangyg.mall.authz.support.authentication.member.MemberAccessAuthenticationFilter;
import com.jiangyg.mall.authz.support.authentication.member.MemberLoginAuthenticationFilter;
import com.jiangyg.mall.authz.support.authentication.member.MemberLogoutHandlerImpl;
import com.jiangyg.mall.authz.support.authentication.member.MemberRequestMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.ExceptionTranslationFilter;

import java.util.ArrayList;
import java.util.List;

/**
 * 类描述：门户网站（会员）安全认证配置
 *
 * @author jiangyg
 * @date 2022-01-08
 */
 @Order(2)
 @Configuration(proxyBeanMethods = false)
public class SecurityMemberConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 认证管理器
     */
    private AuthenticationManager authenticationManager;

    /**
     * 门户网站（会员）权限认证相关实现
     */
    private final MemberUserDetailsService memberUserDetailsService;

    @Autowired
    public SecurityMemberConfiguration(MemberUserDetailsService memberUserDetailsService) {
        this.memberUserDetailsService = memberUserDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 设置门户网站（会员）请求匹配策略
        http.requestMatcher(new MemberRequestMatcher());
        // 禁用 CSRF
        // TODO 研究下，怎么使用
        http.csrf().disable();
        // session 策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        // 禁用缓存
        http.headers().cacheControl();
        // 退出登录路径和退出登录处理器
        http.logout().logoutUrl(SecurityConstant.LOGOUT_URL).addLogoutHandler(logoutHandlerImpl());
        // 登录验证，注意此过滤器应为最前
        http.addFilter(loginAuthenticationFilter());
        // 设置访问权限校验
        // http.authorizeRequests(authorize -> authorize.requestMatchers(new AuthenticationRequestMatcher()).access("@adminAuthenticationService.hasAuthorities(authentication)"));
        // 访问权限前置过滤器（一定要在异常处理过滤器之后，否则异常不能正常处理）
        http.addFilterAfter(accessAuthenticationFilter(), ExceptionTranslationFilter.class);
        // 认证或者权限异常处理
        final ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http.exceptionHandling();
        exceptionHandling.authenticationEntryPoint(new AuthErrorExceptionHandler());
        exceptionHandling.accessDeniedHandler(new AccessDeniedExceptionHandler());
    }

    /**
     * 功能描述：定义访问权限校验
     *
     * @return 访问权限校验
     */
    private MemberAccessAuthenticationFilter accessAuthenticationFilter() {
        return new MemberAccessAuthenticationFilter(authenticationManager());
    }

    /**
     * 功能描述：构建权限管理器
     *
     * @return 权限管理器
     */
    @Override
    protected AuthenticationManager authenticationManager() {
        // 如果已经初始化过则直接返回
        if (this.authenticationManager != null) {
            return this.authenticationManager;
        }
        // 定义权限验证实现列表
        List<AuthenticationProvider> providers = new ArrayList<>();
        // 追加用户名和密码授权器
        providers.add(daoAuthenticationProvider());
        // 返回并初始化认证管理器
        this.authenticationManager = new ProviderManager(providers);
        return this.authenticationManager;
    }

    /**
     * 功能描述：用户名和密码授权器
     *
     * @return 结果
     */
    private DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(memberUserDetailsService);
        return daoAuthenticationProvider;
    }

    /**
     * 功能描述：定义登录认证
     *
     * @return 登录认证
     */
    private MemberLoginAuthenticationFilter loginAuthenticationFilter() {
        return new MemberLoginAuthenticationFilter(authenticationManager());
    }

    /**
     * 功能描述：定义退出登录处理器
     *
     * @return 退出登录处理器
     */
    private MemberLogoutHandlerImpl logoutHandlerImpl() {
        return new MemberLogoutHandlerImpl();
    }

    /**
     * 功能描述：加密器
     *
     * @return 结果
     */
    private BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
